package edu.hawaii.duedates;

import edu.hawaii.duedates.libraries.HawaiiStateLibrary;
import edu.hawaii.duedates.libraries.Library;
import edu.hawaii.duedates.libraries.UhLibrary;
import edu.hawaii.duedates.mail.Email;
import edu.hawaii.duedates.results.ResultsList;
import argparser.ArgParser;
import argparser.BooleanHolder;
import argparser.DoubleHolder;
import argparser.IntHolder;
import argparser.StringHolder;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Checks various libraries and retrieves information on items that the patron has checked out.
 * 
 * @author Erin Kim and Arthur Shum
 */
public class DueDates extends TimerTask {

  Boolean useConsole = false;
  Boolean useEmail = false;
  String emailAddr = "";
  String smtpHost = "";

  String bannerId = "";
  String lastName = "";
  String barcode = "";
  String pin = "";

  String sortBy = "library";
  int withinDays = 0;
  long wakeupInMs = 0;
  Boolean isVerbose = false;

  /**
   * Public boolean to flag the obtaining of Hawaii State Library results list.
   */
  public Boolean getHsl = false;
  /**
   * Public boolean to flag the obtaining of UH Library results list.
   */
  public Boolean getUhm = false;
  Boolean argsOk = false;
  
  String uhmOutput = "";
  String hslOutput = "";


  /**
   * Calls methods to parse user input, run/schedule the tasks to query the libraries and print out
   * the results.
   * 
   * @param args Library sites to query, login credentials, sort/within/verbose options.
   */
  public static void main(String[] args) {
    DueDates duedates = new DueDates();
    try {
      duedates.argparser(args);      
    }
    catch (Exception e) {
      if (duedates.isVerbose) {
        e.printStackTrace();
      }
      else {
        System.out.print(e.getMessage());        
      }
    }

    if (duedates.argsOk) {
      Timer timer = new Timer();
      if (duedates.wakeupInMs > 0) {
        timer.scheduleAtFixedRate(new DueDates(), 0, duedates.wakeupInMs);
      }
      else {
        duedates.run();
        timer.cancel();
      }
    }
  }


  /**
   * Calls methods to gather results, process the results, and print out the results. This is the
   * TimerTask method that gets called from the timer.scheduleAtFixedRate method.
   */
  public void run() {
    ResultsList allResults = getAllResults();

    if (this.withinDays > 0) {
      allResults.filter(this.withinDays);
    }

    allResults.sort(this.sortBy);
    
    if (allResults.isEmpty()) {
      System.out.print("=== No results found ===\n\n");
    }
    else {
      if (this.useConsole) {
        System.out.print(allResults);
      }
      if (this.useEmail) {
        try {
          new Email(this.emailAddr, this.smtpHost, allResults.toString());
          System.out.print("=== Email successfully sent! ===\n\n");
        }
        catch (Exception e) {
          if (this.isVerbose) {
            e.printStackTrace();
          }
          else {
            System.out.println(e.getMessage());
          }
        } // catch
      } // if
    } // else
  }

  
  /**
   * Gets the results for each library and adds them the allItems List.
   * 
   * @return A List of all BorrowedItems.
   */
  public ResultsList getAllResults() {
    ResultsList allResults = new ResultsList();
    if (this.getHsl) {
      ResultsList hawaiiResults = getHawaiiResults();
      allResults.addAll(hawaiiResults);
    }
    if (this.getUhm) {
      ResultsList uhResults = getUhResults();
      allResults.addAll(uhResults);
    }
    return allResults;
  }

  /**
   * Gets checked-out item information for the patron from the Hawaii State Public Library System,
   * and stores the results in a List of BorrowedItem objects. If there are no results, displays a
   * message and returns an empty list.
   * 
   * @return A List of BorrowedItem objects, which hold information about checked-out items.
   */
  private ResultsList getHawaiiResults() {
    ResultsList hawaiiResults = new ResultsList();
    try {
      System.out.print("Processing information for Hawaii State Public Library System ...\n\n");
      Library hsl = new HawaiiStateLibrary(this.barcode, this.pin);
      hawaiiResults = hsl.getInfo();
    }
    catch (Exception e) {
      System.out.print("=== Error getting results for Hawaii State Public Library System ===\n\n");
      if (this.isVerbose) {
        e.printStackTrace();
      }
      else {
        System.out.println(e.getMessage());
      }
    } // catch
    return hawaiiResults;
  }

  /**
   * Gets checked-out item information for the patron from the University of Hawaii Library System,
   * and stores the results in a List of BorrowedItem objects. If there are no results, displays a
   * message and returns an empty list.
   * 
   * @return A List of BorrowedItem objects, which hold information about checked-out items.
   */
  private ResultsList getUhResults() {
    ResultsList uhResults = new ResultsList();
    try {
      System.out.print("Processing information for University of Hawaii Library System ...\n\n");
      Library uhm = new UhLibrary(this.bannerId, this.lastName);
      uhResults = uhm.getInfo();
    }
    catch (Exception e) {
      System.out.print("=== Error getting results for Univ. of Hawaii Library System ===\n\n");
      if (this.isVerbose) {
        e.printStackTrace();
      }
      else {
        System.out.println(e.getMessage());
      }
    } // catch
    return uhResults;
  }

  /**
   * Parses arguments passed to the program, performs some validity checking, and assigns their
   * values to the appropriate fields.
   * 
   * @param args The arguments passed to the program.
   * @throws Exception If there is a problem with the arguments.
   */
  void argparser(String[] args) throws Exception {    
    ArgParser parser = new ArgParser("java -jar /path/to/DueDates.jar <Options>");
    parser.setHelpIndentation(28);
    BooleanHolder console = new BooleanHolder();
    String[] email = new String[2];
    String[] uhm = new String[2];
    String[] hsl = new String[2];
    StringHolder sort = new StringHolder();
    IntHolder within = new IntHolder();
    DoubleHolder wakeup = new DoubleHolder();
    BooleanHolder verbose = new BooleanHolder();

    String consoleOption = "-console %v #Outputs results to the console. Must use either this "
        + "option or -email.";
    String emailOption = "-email %sX2 #<email> <server>#Emails results, if any, to <email> using "
        + "the <server>. Must use either this option or -console.";
    String hslOption = "-hsl %sX2 #<params>#Barcode (10 digits) and pin#. Must use either this "
        + "option or -uhm.";
    String uhmOption = "-uhm %sX2 #<params>#BannerId (8 digits) and LastName. Must use either "
        + "this option or -hsl.";
    String sortOption = "-sort %s{library,duedate}#<{library|duedate}>#Sort the results by "
        + "'library'(default) or by 'duedate'.";
    String withinOption = "-within %i{[0,9999]} #Display only items that are due within "
        + "<integer> days. 0 displays all items (default).";
    String wakeupOption = "-wakeup %f #Run the program every <float> days. 0 runs the program "
        + "once (default).";
    String verboseOption = "-verbose %v #Displays detailed error messages.";

    parser.addOption(consoleOption, console);
    parser.addOption(emailOption, email);
    parser.addOption(hslOption, hsl);
    parser.addOption(uhmOption, uhm);
    parser.addOption(sortOption, sort);
    parser.addOption(withinOption, within);
    parser.addOption(wakeupOption, wakeup);
    parser.addOption(verboseOption, verbose);
    if (args.length == 0) {
      throw new Exception(parser.getHelpMessage());
      // System.out.println(parser.getHelpMessage());
    }    
    else {
      parser.matchAllArgs(args);
      this.useConsole = console.value;
      this.emailAddr = email[0];
      this.smtpHost = email[1];
      this.bannerId = uhm[0];
      this.lastName = uhm[1];
      this.barcode = hsl[0];
      this.pin = hsl[1];
      this.sortBy = sort.value;
      this.withinDays = within.value;
      this.wakeupInMs = (long) (1000 * 60 * 60 * 24 * wakeup.value);
      this.isVerbose = verbose.value;

      // not implemented:
      // check emailAddr is in the correct form
      // check smtpServer is in the correct form
      // check bannerId is 8 digits
      // check barcode is 10 digits

      if (this.bannerId != null && this.lastName != null) {
        this.getUhm = true;
      }
      if (this.barcode != null && this.pin != null) {
        this.getHsl = true;
      }
      if (this.emailAddr != null && this.smtpHost != null) {
        this.useEmail = true;
      }

      this.argsOk = ((this.useConsole || this.useEmail) && (this.getUhm || this.getHsl));

      if (!this.argsOk) {
        throw new Exception(parser.getHelpMessage());
        // System.out.println(parser.getHelpMessage());
      }
    }
  }

  // The following getters and setters have been placed to replace the functionality 
  // of the wicket web application.  In the case of a web app, the arg paser is
  // irrelevant and getters and setters are used in place.

  /**
   * Sets the banner id.
   * @param string The banner ID to be set.
   */
  public void setBannerId(String string) {
    this.bannerId = string;
  }

  /**
   * Sets the email address.
   * @param emailAddr The email address to be set.
   */
  public void setEmailAddr(String emailAddr) {
    this.emailAddr = emailAddr;
  }

  /**
   * Sets the lastname.
   * @param lastName The last name to be set.
   */
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  /**
   * Sets the barcode.
   * @param barcode The barcode to be set.
   */
  public void setBarcode(String barcode) {
    this.barcode = barcode;
  }

  /**
   * Sets the pin.
   * @param pin The pin to be set.
   */
  public void setPin(String pin) {
    this.pin = pin;
  }

  /**
   * Sets the sortby method.
   * @param sortBy The method of sort.
   */
  public void setSortBy(String sortBy) {
    this.sortBy = sortBy;
  }

  /**
   * Sets the withingdays variable.
   * @param withinDays The amount of days.
   */
  public void setWithinDays(int withinDays) {
    this.withinDays = withinDays;
  }
  
  /**
   * Gets the formated UHM Library output.
   * @return The formated UHM Library output.
   */
  public String getUhmOutput() {
    return uhmOutput;
  }

  /**
   * Sets the UHM Library output string.
   * @param uhmOutput The formated UHM Library output string.
   */
  public void setUhmOutput(String uhmOutput) {
    this.uhmOutput = uhmOutput;
  }

  /**
   * Gets the Hawaii State Library formated output string.
   * @return hslOutput The formated HS Library output string.
   */
  public String getHslOutput() {
    return hslOutput;
  }

  /**
   * Sets the Hawaii State Library formated output string.
   * @param hslOutput The formated HS Library
   */
  public void setHslOutput(String hslOutput) {
    this.hslOutput = hslOutput;
  }

} // class
